﻿using System;
using System.Diagnostics;
using System.Reflection;
using PostSharp.Aspects;

namespace Theoreme.Teaching.AOP.Sample01
{
    /// <summary>
    /// Aspect that, when applied on a method, catches all its exceptions,
    /// assign them a GUID, log them, and replace them by an <see cref="InternalException"/>.
    /// </summary>
    /// <remarks>
    /// Exception monitoring is a special case of logging. 
    /// Typically, unhandled exceptions must be caught before they are 
    /// allowed to crash the application or get passed to the caller. 
    /// 
    /// Quite often, exceptions need to be wrapped before they cross the 
    /// application boundary, because the exception may contain details that 
    /// could unveil security leaks, making life easier for potential attackers. 
    /// Therefore, exception monitoring is often part of a larger exception 
    /// handling policy.
    /// 
    /// The following example illustrates an easy exception handling policy: 
    ///     we log the exception (to record all details) and to wrap it 
    ///     into an InternalException
    /// </remarks>
    
    [Serializable]
    public sealed class WrapExceptionAttribute : OnExceptionAspect
    {
        [NonSerialized] private readonly Type exceptionType;

        /// <summary>
        /// Initializes a new instance of the <see cref="WrapExceptionAttribute"/> class.
        /// </summary>
        public WrapExceptionAttribute()
        {}

        /// <summary>
        /// Initializes a new instance of the <see cref="WrapExceptionAttribute"/> class.
        /// </summary>
        /// <param name="exceptionType">Type of the exception.</param>
        public WrapExceptionAttribute( Type exceptionType )
        {
            this.exceptionType = exceptionType;
        }

        /// <summary>
        /// Gets the type of exception handled by this aspect.
        /// </summary>
        /// <param name="targetMethod">Method to which the current aspect is applied.</param>
        /// <returns>
        /// The type (derived from <see cref="T:System.Exception"/>) of exceptions handled
        /// by this aspect.
        /// </returns>
        public override Type GetExceptionType( MethodBase targetMethod )
        {
            return this.exceptionType;
        }



        /// <summary>
        /// Method invoked upon failure of the method to which the current
        /// aspect is applied.
        /// </summary>
        /// <param name="args">Information about the method being executed.</param>
        public override void OnException( MethodExecutionArgs args )
        {
            // Create a unique identifier for this exception.
            Guid guid = Guid.NewGuid();

            // Trace the exception to log.
            Trace.TraceError( "Exception {0}: {1}", guid, args.Exception );


            // Replace the exception by a BusinessException.
            args.Exception =
                new BusinessException( string.Format( "An unexpected error has occurred. Please contact the system administrator and report the number {0}.",
                                                      guid ) );

            args.FlowBehavior = FlowBehavior.ThrowException;
        }
    }
}